package com.example.cloud.service.personal.impl;

import com.example.cloud.constants.CommonConstants;
import com.example.cloud.constants.StateConstants;
import com.example.cloud.dao.personal.AttendanceRepository;
import com.example.cloud.model.dto.Result;
import com.example.cloud.model.personal.AttendanceRecord;
import com.example.cloud.model.system.SystemConfig;
import com.example.cloud.model.system.User;
import com.example.cloud.service.personal.AttendanceService;
import com.example.cloud.service.system.OrganService;
import com.example.cloud.service.system.SystemConfigService;
import com.example.cloud.service.system.UserService;
import com.example.cloud.utils.BusinessUtil;
import com.example.cloud.utils.DateUtils;
import com.example.cloud.utils.RequestUtil;
import com.example.cloud.utils.ResultUtil;
import org.apache.commons.lang.time.DateFormatUtils;
import org.beetl.sql.core.SQLManager;
import org.beetl.sql.core.SQLReady;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by wangtao on 2018/5/22.
 */
@Service
public class AttendanceServiceImpl implements AttendanceService {

    @PersistenceContext
    EntityManager em;

    @Autowired
    private SQLManager sqlManager;

    @Autowired
    private UserService userService;

    @Autowired
    private AttendanceRepository attendanceRepository;

    @Autowired
    private SystemConfigService configService;

    @Autowired
    private OrganService organService;

    @Override
    public Result doSave(AttendanceRecord record) throws ParseException {
        // 外出不需要校验
        if(record.getType() != 10 && record.getUserId() == null){
            Result result = checkAttendance(record);
            if(result.getCode() != 0){
                return result;
            }
        }
        User user = RequestUtil.getLoginUser();
        if (record.getId() == null) {
            record.setStartTime(DateUtils.parseDate(record.getStartTimeStr(), CommonConstants.DATE_FORMATS));
            record.setEndTime(DateUtils.parseDate(record.getEndTimeStr(), CommonConstants.DATE_FORMATS));
            record.setRecordType(StateConstants.ATTENDANCE_RECORD_TYPE_LEAVE);
            record.setAccountId(user.getAccountId());
            if(record.getUserId() == null){
                // 个人请假
                record.setUserId(user.getId());
                record.setUserName(user.getUsername());
            }else{
                // 管理员上请假记录
                record.setStatus(StateConstants.ATTENDANCE_STATUS_AGREE);
            }
            record.setOrganId(user.getOrganId());
            record.setOrganName(user.getOrganName());
        }
        BusinessUtil.appendRecord(record);
        return ResultUtil.success(attendanceRepository.save(record));
    }

    /**
     * 校验请假条件
     *
     * @return
     */
    public Result checkAttendance(AttendanceRecord record) {
        SystemConfig c = configService.findOne();
        if (c != null) {
            // 需要提前申请的小时数
            int at = c.getAdvanceTime();
            // 请假开始时间
            Date st = record.getStartTime();
            // 通过当前时间得出检验时间
            Date ct = DateUtils.addHours(new Date(), at);
            // 如果请假开始时间小于检验时间，则不允许请假
            if (st.compareTo(ct) < 0) {
                return ResultUtil.fail("请提前" + at + "小时提交请假申请");
            }
        }
        return ResultUtil.success();
    }

    @Override
    @Transactional
    public void doRelaxation(AttendanceRecord record) {
        // 更新请假记录为已调休
        Long id = record.getId();
        attendanceRepository.updateStatus(StateConstants.ATTENDANCE_STATUS_RELAXATION, id);
        // 创建调休记录
        record.setId(null);// 清空主键
        record.setStatus(StateConstants.ATTENDANCE_STATUS_WAIT);
        record.setRecordType(StateConstants.ATTENDANCE_RECORD_TYPE_RELAXATION);// 标识为调休记录
        record.setParentId(id);// 关联请假记录
        BusinessUtil.appendRecord(record);
        attendanceRepository.save(record);
    }

    @Override
    public List checkAgree(List ids) {
        StringBuilder sql = new StringBuilder("select id from attendance_record where id in(:ids) and status = 0");
        Query query = em.createNativeQuery(sql.toString());
        query.setParameter("ids", ids);
        return query.getResultList();
    }

    @Override
    @Transactional
    public void doAgree(List ids) {
        StringBuilder sql = new StringBuilder("update attendance_record set status = :status where id in(:ids)");
        Query query = em.createNativeQuery(sql.toString());
        query.setParameter("status", StateConstants.ATTENDANCE_STATUS_AGREE);
        query.setParameter("ids", ids);
        query.executeUpdate();
    }

    @Override
    public boolean checkRefuse(Long id) {
        AttendanceRecord record = attendanceRepository.findOne(id);
        if (record.getStatus() == 0) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    @Transactional
    public void doRefuse(Long id, String denialReason) {
        StringBuilder sql = new StringBuilder("update attendance_record set status = :status,denial_reason = :denialReason where id = :id");
        Query query = em.createNativeQuery(sql.toString());
        query.setParameter("status", StateConstants.ATTENDANCE_STATUS_REFUSE);
        query.setParameter("denialReason", denialReason);
        query.setParameter("id", id);
        query.executeUpdate();
    }

    @Override
    public List<User> getHealthcare(List<Long> ids, String datetime) {
        // 获取所有医护工作人员集合
        String idsStr = "";
        List<User> users = userService.findHealthcareFromCache(ids);
        for (User user : users) {
            idsStr += "," + user.getId();
        }
        idsStr = idsStr.replaceFirst(",", "");
        // 查询当天休息的
        StringBuilder sql = new StringBuilder("select user_id as userId from attendance_record");
        sql.append(" where user_id in(" + idsStr + ")");
        sql.append(" and record_type = " + StateConstants.ATTENDANCE_RECORD_TYPE_LEAVE);
        sql.append(" and status = " + StateConstants.ATTENDANCE_STATUS_AGREE);
        sql.append(" and start_time <= '" + datetime + "'");
        sql.append(" and end_time >= '" + datetime + "'");
        List<Long> canNotList = new ArrayList<>();
        List<Map> mapList = sqlManager.execute(new SQLReady(sql.toString()), Map.class);
        for (Map map : mapList) {
            canNotList.add(Long.valueOf(map.get("userId").toString()));
        }
        // 过滤掉不符合条件的
        List<User> resultList = new ArrayList<>();
        for (User user : users) {
            if (!canNotList.contains(user.getId())) {
                resultList.add(user);
            }
        }
        return resultList;
    }

    @Override
    public List<User> getHealthcare(List<Long> ids, String dateStr, int appointType, int process) throws ParseException {
        List<User> resultList = new ArrayList<>();
        List<Long> canNotList = new ArrayList<>();
        String organIds = "";
        for (Long id : ids) {
            organIds += "," + id;
        }
        organIds = organIds.replaceFirst(",", "");
        // 只有预约到店的需要限制预约次数
        if (process == StateConstants.RESERVATION_PROCESS_ARRIVAL) {
            SystemConfig config = configService.findOne();
            if (config != null) {
                // 日期字符串转换成日期类型
                Date appointment = DateUtils.parseDate(dateStr, new String[]{"yyyy-MM-dd HH:mm", "yyyy-MM-dd HH:mm:ss"});
                // 预约时间年月日
                String ymd = dateStr.substring(0, dateStr.indexOf(" "));
                if (config.getDoctorReceptionOld() > 0 || config.getDoctorReceptionNew() > 0) {
                    // 医生接诊时间间隔
                    int hours = config.getDoctorInterval();
                    // 开始工作时间
                    Date startDate = DateUtils.parseDate(ymd + " 10:00:00", new String[]{"yyyy-MM-dd HH:mm:ss"});
                    Date endDate = DateUtils.addHours(startDate, hours);
                    while (true) {
                        // 大于计算开始时间，小于计算结束时间，表示已匹配到计算时区
                        if (appointment.compareTo(startDate) >= 0 && appointment.compareTo(endDate) < 0) {
                            break;
                        }
                        startDate = DateUtils.addHours(startDate, hours);
                        endDate = DateUtils.addHours(endDate, hours);
                    }
                    String startDateStr = DateFormatUtils.format(startDate, "yyyy-MM-dd HH:mm:ss");
                    String endDateStr = DateFormatUtils.format(endDate, "yyyy-MM-dd HH:mm:ss");
                    // 查询所有医生在预约时间当天的预约次数
                    StringBuilder sql = new StringBuilder("select doctor_id as userId,type,COUNT(1) as count from business_visit_record");
                    sql.append(" where organ_id in (" + organIds + ")");
                    sql.append(" and appointment BETWEEN '" + startDateStr + "' and '" + endDateStr + "'");
                    sql.append(" GROUP BY doctor_id,type");
                    List<Map> mapList = sqlManager.execute(new SQLReady(sql.toString()), Map.class);
                    // 复诊预约
                    if (appointType == StateConstants.RESERVATION_TYPE_OLD && config.getDoctorReceptionOld() > 0) {
                        for (Map map : mapList) {
                            Long userId = Long.valueOf(map.get("userId").toString());
                            int type = Integer.parseInt(map.get("type").toString());
                            // 已预约次数
                            int count = Integer.parseInt(map.get("count").toString());
                            if (appointType == type) {
                                if (count >= config.getDoctorReceptionOld()) {
                                    canNotList.add(userId);
                                }
                            }
                        }
                        // 新诊预约
                    } else if (appointType == StateConstants.RESERVATION_TYPE_NEW && config.getDoctorReceptionNew() > 0) {
                        for (Map map : mapList) {
                            Long userId = Long.valueOf(map.get("userId").toString());
                            int type = Integer.parseInt(map.get("type").toString());
                            // 已预约次数
                            int count = Integer.parseInt(map.get("count").toString());
                            if (appointType == type) {
                                if (count >= config.getDoctorReceptionOld()) {
                                    canNotList.add(userId);
                                }
                            }
                        }
                    }
                }
                if (config.getNurseReceptionOld() > 0 || config.getNurseReceptionNew() > 0) {
                    // 护士接诊时间间隔
                    int hours = config.getNurseInterval();
                    // 开始工作时间
                    Date startDate = DateUtils.parseDate(ymd + " 10:00:00", new String[]{"yyyy-MM-dd HH:mm:ss"});
                    Date endDate = DateUtils.addHours(startDate, hours);
                    while (true) {
                        // 大于计算开始时间，小于计算结束时间，表示已匹配到计算时区
                        if (appointment.compareTo(startDate) >= 0 && appointment.compareTo(endDate) < 0) {
                            break;
                        }
                        startDate = DateUtils.addHours(startDate, hours);
                        endDate = DateUtils.addHours(endDate, hours);
                    }
                    String startDateStr = DateFormatUtils.format(startDate, "yyyy-MM-dd HH:mm:ss");
                    String endDateStr = DateFormatUtils.format(endDate, "yyyy-MM-dd HH:mm:ss");
                    // 查询所有护士在预约时间当天的预约次数
                    StringBuilder sql = new StringBuilder("select nurse_id as userId,type,COUNT(1) as count from business_visit_record");
                    sql.append(" where organ_id in (" + organIds + ")");
                    sql.append(" and appointment BETWEEN '" + startDateStr + "' and '" + endDateStr + "'");
                    sql.append(" GROUP BY nurse_id,type");
                    List<Map> mapList = sqlManager.execute(new SQLReady(sql.toString()), Map.class);
                    // 复诊预约
                    if (appointType == StateConstants.RESERVATION_TYPE_OLD && config.getNurseReceptionOld() > 0) {
                        for (Map map : mapList) {
                            Long userId = Long.valueOf(map.get("userId").toString());
                            int type = Integer.parseInt(map.get("type").toString());
                            // 已预约次数
                            int count = Integer.parseInt(map.get("count").toString());
                            if (appointType == type) {
                                if (count >= config.getNurseReceptionOld()) {
                                    canNotList.add(userId);
                                }
                            }
                        }
                        // 新诊预约
                    } else if (appointType == StateConstants.RESERVATION_TYPE_NEW && config.getNurseReceptionNew() > 0) {
                        for (Map map : mapList) {
                            Long userId = Long.valueOf(map.get("userId").toString());
                            int type = Integer.parseInt(map.get("type").toString());
                            // 已预约次数
                            int count = Integer.parseInt(map.get("count").toString());
                            if (appointType == type) {
                                if (count >= config.getNurseReceptionNew()) {
                                    canNotList.add(userId);
                                }
                            }
                        }
                    }
                }
            }
        }
        // 获取所有医护工作人员集合
        String idsStr = "";
        List<User> users = userService.findHealthcareFromCache(ids);
        for (User user : users) {
            idsStr += "," + user.getId();
        }
        idsStr = idsStr.replaceFirst(",", "");
        // 查询当天休息的
        StringBuilder sql = new StringBuilder("select user_id as userId from attendance_record");
        sql.append(" where user_id in(" + idsStr + ")");
        sql.append(" and record_type = " + StateConstants.ATTENDANCE_RECORD_TYPE_LEAVE);
        sql.append(" and status = " + StateConstants.ATTENDANCE_STATUS_AGREE);
        sql.append(" and start_time <= '" + dateStr + "'");
        sql.append(" and end_time >= '" + dateStr + "'");
        List<Map> mapList = sqlManager.execute(new SQLReady(sql.toString()), Map.class);
        for (Map map : mapList) {
            canNotList.add(Long.valueOf(map.get("userId").toString()));
        }
        // 过滤掉不符合条件的
        List<User> doctorList = new ArrayList<>();
        List<User> nurseList = new ArrayList<>();
        for (User user : users) {
            if (!canNotList.contains(user.getId())) {
                resultList.add(user);
            }
        }
        // 返回符合条件的
        return resultList;
    }

    @Override
    public List<List<AttendanceRecord>> findAllByCurrentDate(Date date, User user, Long userId) {
        String today = DateUtils.DATE_FORMAT.format(new Date());
        List<List<AttendanceRecord>> resultList = new ArrayList<List<AttendanceRecord>>();
        List<AttendanceRecord> list = new ArrayList<AttendanceRecord>();
        int size = 0;
        try {
            Date firstDay = DateUtils.DATE_FORMAT.parse(DateUtils.getFirstDay(date));
            int week = DateUtils.getWeek(firstDay);
            if (week == 0) {
                size = 6;
            } else if (week == 2) {
                size = 1;
            } else if (week == 3) {
                size = 2;
            } else if (week == 4) {
                size = 3;
            } else if (week == 5) {
                size = 4;
            } else if (week == 6) {
                size = 5;
            }
            for (int i = 0; i < size; i++) {
                AttendanceRecord recruitment = new AttendanceRecord();
                recruitment.setDay(-1);
                list.add(recruitment);
            }
            String strStartTime = DateUtils.getFirstDay(date);
            strStartTime += " 00:00:00";
            String strEndTime = DateUtils.getLastDay(date);
            strEndTime += " 23:59:59";
            Date startTime = DateUtils.DATETIME_FORMAT.parse(strStartTime);
            Date endTime = DateUtils.DATETIME_FORMAT.parse(strEndTime);
            Map<String, List<AttendanceRecord>> resultMap = findAllByMap(user.getAccountId(), startTime, endTime, userId);
            int endDayNumber = DateUtils.getLastDayToNumber(date);
            for (int i = 0; i < endDayNumber; i++) {
                Date monthDay = DateUtils.addDays(firstDay, i);
                AttendanceRecord recruitment = new AttendanceRecord();
                String strMonthDay = DateUtils.DATE_FORMAT.format(monthDay);
                if (resultMap.containsKey(strMonthDay)) {
                    List<AttendanceRecord> records = resultMap.get(strMonthDay);
                    recruitment.setRecruitmentList(records);
                }
                recruitment.setDay(DateUtils.getDay(monthDay));
                if (strMonthDay.equals(today)) {
                    recruitment.setIsDay(1);// 表示为当天
                }
                list.add(recruitment);
                if (list.size() == 7) {
                    resultList.add(list);
                    list = new ArrayList<AttendanceRecord>();
                }
            }
            if (list.size() > 0) {
                if (list.size() == 1) {
                    size = 6;
                } else if (list.size() == 2) {
                    size = 5;
                } else if (list.size() == 3) {
                    size = 4;
                } else if (list.size() == 4) {
                    size = 3;
                } else if (list.size() == 5) {
                    size = 2;
                } else if (list.size() == 6) {
                    size = 1;
                }
                for (int i = 0; i < size; i++) {
                    AttendanceRecord record = new AttendanceRecord();
                    record.setDay(-1);
                    list.add(record);
                }
                resultList.add(list);
            }
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return resultList;
    }

    public Map<String, List<AttendanceRecord>> findAllByMap(Long accountId, Date startTime, Date endTime, Long userId) throws ParseException {
        SystemConfig config = configService.findOne();
        SimpleDateFormat sf = new SimpleDateFormat("HH:mm");
        Map<String, List<AttendanceRecord>> resultMap = new HashMap<String, List<AttendanceRecord>>();
        List<AttendanceRecord> records = findAll(accountId, userId, startTime, endTime);
        for (AttendanceRecord record : records) {
            Date _startTime = record.getStartTime();
            Date _endTime = record.getEndTime();
            int days = DateUtils.subDay(_endTime, _startTime).intValue();
            String dateKey = DateUtils.DATE_FORMAT.format(_startTime);
            record.setStartTimeStr(sf.format(record.getStartTime()));
            if (days >= 1) {
                record.setEndTimeStr(config.getEndTime());
            } else {
                record.setEndTimeStr(sf.format(record.getEndTime()));
            }
            if(record.getType() == 0) {
                record.setStartTimeStr("休息："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 1){
                record.setStartTimeStr("调休："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 2){
                record.setStartTimeStr("事假："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 3){
                record.setStartTimeStr("病假："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 4){
                record.setStartTimeStr("年假："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 5){
                record.setStartTimeStr("婚假："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 6){
                record.setStartTimeStr("产假："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 7){
                record.setStartTimeStr("陪产假："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 8){
                record.setStartTimeStr("路途假："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 9){
                record.setStartTimeStr("其他："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }else if(record.getType() == 10){
                record.setStartTimeStr("外出："+record.getStartTimeStr() + " - " + record.getEndTimeStr());
            }
            if (resultMap.containsKey(dateKey)) {
                resultMap.get(dateKey).add(record);
            } else {
                List<AttendanceRecord> list = new ArrayList<AttendanceRecord>();
                list.add(record);
                resultMap.put(dateKey, list);
            }
            for (int i = 1; i <= days; i++) {
                _startTime = DateUtils.addDays(_startTime, 1);
                dateKey = DateUtils.DATE_FORMAT.format(_startTime);
                AttendanceRecord _recruitment = new AttendanceRecord();
                _recruitment.setUserName(record.getUserName());
                _recruitment.setStartTimeStr(config.getStartTime());
                if (i < days) {
                    _recruitment.setEndTimeStr(config.getEndTime());
                } else {
                    _recruitment.setEndTimeStr(sf.format(record.getEndTime()));
                }
                if(_recruitment.getType() == 0) {
                    _recruitment.setStartTimeStr("休息："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 1){
                    _recruitment.setStartTimeStr("调休："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 2){
                    _recruitment.setStartTimeStr("事假："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 3){
                    _recruitment.setStartTimeStr("病假："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 4){
                    _recruitment.setStartTimeStr("年假："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 5){
                    _recruitment.setStartTimeStr("婚假："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 6){
                    _recruitment.setStartTimeStr("产假："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 7){
                    _recruitment.setStartTimeStr("陪产假："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 8){
                    _recruitment.setStartTimeStr("路途假："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 9){
                    _recruitment.setStartTimeStr("其他："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }else if(_recruitment.getType() == 10){
                    _recruitment.setStartTimeStr("外出："+_recruitment.getStartTimeStr() + " - " + _recruitment.getEndTimeStr());
                }
                if (resultMap.containsKey(dateKey)) {
                    resultMap.get(dateKey).add(_recruitment);
                } else {
                    List<AttendanceRecord> list = new ArrayList<AttendanceRecord>();
                    list.add(_recruitment);
                    resultMap.put(dateKey, list);
                }
            }
        }
        return resultMap;
    }

    public List<AttendanceRecord> findAll(Long accountId, Long userId, Date startTime, Date endTime){
        StringBuilder sql = new StringBuilder("select * from attendance_record");
        sql.append(" where account_id = :accountId");
        if(userId != null && userId != 0){
            sql.append(" and user_id = :userId");
        }
//        sql.append(" and ((record_type = :recordType1 and parent_id is null) or (record_type = :recordType2 and parent_id is not null))");
        sql.append(" and `status` = :status");
        sql.append(" and ((start_time BETWEEN :startTime and :endTime) or (end_time BETWEEN :startTime and :endTime))");
        sql.append(" and delete_flag = 0");
        Query query = em.createNativeQuery(sql.toString(), AttendanceRecord.class);
        query.setParameter("accountId", accountId);
        if(userId != null && userId != 0){
            query.setParameter("userId", userId);
        }
//        query.setParameter("recordType1", StateConstants.ATTENDANCE_RECORD_TYPE_LEAVE);
//        query.setParameter("recordType2", StateConstants.ATTENDANCE_RECORD_TYPE_RELAXATION);
        query.setParameter("status", StateConstants.ATTENDANCE_STATUS_AGREE);
        query.setParameter("startTime", startTime);
        query.setParameter("endTime", endTime);
        return (List<AttendanceRecord>)query.getResultList();
    }

    @Override
    @Transactional
    public void doDelete(List<Long> ids) {
        attendanceRepository.deleteByIdIn(ids);
    }
}
